#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <iostream>
#include <string>


#define MAXSZIEX 128
#define MAXARGS 32

//SHELL  自己内部维护的第一张表：命令行参数表
//故意设置成全局变量
char *argv[MAXARGS];
int argc = 0;
const char *SEP = " "; //注意是字符串，不是字符

//环境变量表
char* genv[MAXSZIEX];
int genvc = 0;

void Loadenv()
{
   // 常规情况，环境变量表是从配置文件来的
   // 今天我们从父进程拷贝
   char ** environ;
   for(;environ[genvc]; genvc++)
   {
     genv[genvc] = new char[100];
     strcpy(genv[genvc], environ[genvc]);
   }
}
static std::string rfindDir(const std::string &p)
{
  if(p == "/")
    return p;
  const std::string psep = "/";
  auto pos = p.rfind(psep);
  if(pos == std::string::npos)
    return std::string();
  return p.substr(pos+1);   // home/whb
}

// 我们shell自己所处的工作路径
char cwd[MAXSZIEX];

// 最近一个命令执行完毕，退出码
int  lastcode = 0;

const char *GetUserName()
{
  char *name = getenv("USER");
  if(name == NULL)
    return "None";
  return name;
}

const char *GetHomeName()
{
  char *hostname = getenv("HOSTNAME");
  if(hostname == NULL)
    return "None"; //绝对路径！怎么样只保留last
  return hostname;
}

const char *GetPwd()
{
  char *pwd = getenv("PWD");
  //char *pwd = getcwd(cwd, sizeof(cwd));
  if(pwd == NULL)
    return "None";
  return pwd;
}

void PrintCommandLine()
{
  //用户名@ 主机名 当前路径
  printf("[%s@%s %s]#", GetUserName(), GetHomeName(),rfindDir( GetPwd()).c_str());
  fflush(stdout);
}

int GetCommand(char commandline[], int size)
{
      if(NULL == fgets(commandline, size, stdin))
        return 0;
      //2.1 用户在输入的时候，至少会按一次回车\n : abcd\n
      commandline[strlen(commandline)-1] = '\0';
      return strlen(commandline);
}

int ParseCommand(char commandline[])
{
    argc = 0;
    memset(argv, 0, sizeof(argv));
    //故意写成 =
    //ls -a -l
    argv[0] = strtok(commandline, SEP);
    while((argv[++argc] = strtok(NULL, SEP)));

    //printf("agrc: %d\n", argc);
    //int i = 0;
    //for(; argv[i]; i++)
    //  printf("agrv[%d]: %s\n", i, argv[i]);

    return 0;
}

//return val
//0: 不是内建命令
//1: 内建命令 && 执行完毕
int CheckBuiltinExecute()
{
    if(strcmp(argv[0], "cd") == 0)
    {
      //内建命令
      if(argc == 2)
      {
        //新的目标路径：argv[1]
        //1.更改进程内核中的路径
        chdir(argv[1]);
        //2.更改环境变量
        char pwd[1024];
        getcwd(pwd, sizeof(pwd));//获取了当前工作路径到pwd中 ：home/whb

        snprintf(cwd, sizeof(cwd), "PWD=%s", pwd);//把格式化的pwd字符串写入cwd
        putenv(cwd);//加入到环境变量i
        lastcode = 0;
      }
      return 1;
    }
    else if(strcmp(argv[0], "echo") == 0)
    {
      //内建命令
      if(argc == 2)
      {
        //echo helloworld  $?
        if(argv[1][0] == '$')
        {
            if(strcmp(argv[1]+1, "?") == 0)
            {
              printf("lastcode: %d\n", lastcode);
            }
            else if(strcmp(argv[1]+1, "PATH") == 0)
            {
              printf("%s\n", getenv("PATH"));
            }
            lastcode = 0;
        }
        return 1;
      }
    }

    return 0;
}

int ExecuteCommand()
{
  //能不能让你的bash自己执行： ls -a -l
  pid_t id = fork();
  if(id < 0)
     return -1;
  else if(id == 0)
  {
    //子进程:选择替换函数执行 execvp
    execvp(argv[0], argv);
    exit(1);
    
    exit(0); 
  }
  else
  {
      //父进程等待子进程执行完
      int status = 0;
      pid_t rid = waitpid(id, &status, 0);
      if(rid > 0)
      {
        lastcode = WEXITSTATUS(status);
        //printf("wait child process success!");
        //等待成功 todo
      }
  }
}

int main()
{
    //0.从配置文件中获取环境变量填充环境变量表
    Loadenv();
    char command_line[MAXSZIEX];
    while(1)
    {
      //1.打印命令行字符串
      PrintCommandLine();
      //2.获取用户输入
      if(0 == GetCommand(command_line, sizeof(command_line)))
          continue;

      //printf("%s\n", command_line);

      //3. 解析字符串  —>"ls -a -l" 把字符串打散
      ParseCommand(command_line);
      
      //4.这个命令，到底是让父进程bash去执行(内建命令)？还是子进程去执行？？
      if(CheckBuiltinExecute()) //  > 0
          continue;
      
      //5.执行这个命令 ——暂时
      ExecuteCommand();
    }
    return 0;
}
